package com.evildog.workspace.dependencies.mybatis.util;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.evildog.workspace.common.exception.ServerErrorException;
import com.evildog.workspace.common.object.PageQuery;
import com.evildog.workspace.common.util.SpringContextHolder;
import com.evildog.workspace.dependencies.mybatis.annotation.QueryCondition;
import com.evildog.workspace.dependencies.mybatis.annotation.QueryTableLogic;
import com.evildog.workspace.dependencies.mybatis.annotation.QueryUserCondition;
import com.evildog.workspace.dependencies.mybatis.enums.ComparisonOperatorsEnum;
import com.evildog.workspace.dependencies.mybatis.enums.IsDeletedEnum;
import com.evildog.workspace.dependencies.mybatis.enums.MybatisCustomExceptionDesc;
import com.evildog.workspace.dependencies.mybatis.enums.SortOrderEnum;
import com.evildog.workspace.dependencies.mybatis.object.QueryConditionDTO;
import com.evildog.workspace.security.jwt.util.SecurityUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
public class QueryConditionUtils {


    public static <T> IPage<T> getPagination(PageQuery pageQuery) {
        //设置查询数量的初始值
        if (pageQuery.getPageLimit() == null || pageQuery.getPageLimit() <= 0) {
            pageQuery.setPageLimit(10);
        }

        //设置页码的初始值
        if (pageQuery.getPageNum() == null || pageQuery.getPageNum() <= 0) {
            pageQuery.setPageNum(1);
        }
        IPage<T> page = new Page<>(pageQuery.getPageNum(), pageQuery.getPageLimit());

        List<String> sorts = pageQuery.getSorts();
        if (CollectionUtil.isNotEmpty(sorts)) {
            List<OrderItem> orderItems = page.orders();
            for (String sort : sorts) {
                String order = sort.substring(0, 1);

                // 驼峰转下划线
                String fieldName = StrUtil.toUnderlineCase(sort.substring(1));

                OrderItem orderItem = new OrderItem();
                orderItem.setColumn(fieldName);

                if (SortOrderEnum.ASC.getOrder().equals(order)) {
                    orderItem.setAsc(true);
                } else if (SortOrderEnum.DESC.getOrder().equals(order)) {
                    orderItem.setAsc(false);
                } else {
                    // 默认升序
                    orderItem.setAsc(true);
                }

                orderItems.add(orderItem);
            }
        }

        return page;
    }

    public static <T> List<QueryConditionDTO> getQueryCondition(T t) {
        List<QueryConditionDTO> list = new ArrayList<>();
        try {
            // 判断该实体类是否有逻辑删除字段
            initTableLogicCondition(list, t);
            // 设置userId查询条件
            initUserConditionCondition(list, t);
            // 自定义查询条件
            initQueryCondition(list, t);
            return list;
        } catch (IllegalAccessException e) {
            log.error("解析字段值异常:", e);
            throw new ServerErrorException(MybatisCustomExceptionDesc.QUERY_CONDITION_PARSE_ERROR);
        }
    }

    private static <T> void initQueryCondition(List<QueryConditionDTO> list, T t) throws IllegalAccessException {
        // 解析字段查询参数
        Field[] fields = ReflectUtil.getFields(t.getClass());
        for (Field field : fields) {
            field.setAccessible(true);
            Object fieldValue = field.get(t);
            if (isIgnore(fieldValue)) {
                continue;
            }

            // 查询参数解析
            QueryCondition queryCondition = AnnotationUtils.getAnnotation(field, QueryCondition.class);
            if (queryCondition != null) {
                QueryConditionDTO dto = new QueryConditionDTO();
                String fieldName = queryCondition.fieldName();
                if (StrUtil.isBlank(fieldName)) {
                    fieldName = StrUtil.toUnderlineCase(field.getName());
                }

                String tableAlias = queryCondition.tableAlias();

                // 存在别名的情况下，加上.
                if (StrUtil.isNotBlank(tableAlias)) {
                    tableAlias += ".";
                }

                ComparisonOperatorsEnum operator = queryCondition.operator();
                String compare = operator.getOperator();
                switch (operator) {
                    case LIKE:
                        fieldValue = "%" + fieldValue + "%";
                        break;
                    case RIGHT_LIKE:
                        fieldValue = "%" + fieldValue;
                        break;
                    case LEFT_LIKE:
                        fieldValue = fieldValue + "%";
                        break;
                    default:
                        break;
                }

                dto.setFieldName(fieldName)
                        .setTableAlias(tableAlias)
                        .setCompare(compare)
                        .setFieldValue(fieldValue);

                list.add(dto);
            }
        }
    }

    private static <T> void initTableLogicCondition(List<QueryConditionDTO> list, T t) {
        QueryTableLogic tableLogic = AnnotationUtils.getAnnotation(t.getClass(), QueryTableLogic.class);
        if (tableLogic != null) {
            String[] tableAliasArray = tableLogic.value();
            String logicField = tableLogic.logicField();
            String[] logicFieldArray = tableLogic.logicFieldArray();

            boolean hasLogicFieldArray = false;
            if (logicFieldArray.length > 0) {
                Assert.isTrue(logicFieldArray.length == tableAliasArray.length, "指定的删除字段与表别名长度不一致!");
                hasLogicFieldArray = true;
            }

            String fieldName;

            MybatisPlusProperties mybatisPlusProperties = SpringContextHolder.getBean(MybatisPlusProperties.class);
            String mybatisLogicDeleteField = mybatisPlusProperties.getGlobalConfig().getDbConfig().getLogicDeleteField();

            // logicField优先级 指定的logicField -> mybatisLogicDeleteField -> 默认 is_deleted
            if (StrUtil.isNotBlank(logicField)) {
                fieldName = logicField;
            } else if (StrUtil.isNotBlank(mybatisLogicDeleteField)) {
                fieldName = mybatisLogicDeleteField;
            } else {
                fieldName = "is_deleted";
            }


            if (ArrayUtil.isNotEmpty(tableAliasArray)) {
                int fieldIndex = 0;

                for (String tableAlias : tableAliasArray) {
                    if (StrUtil.isNotBlank(tableAlias)) {
                        tableAlias += ".";
                    }
                    QueryConditionDTO dto = new QueryConditionDTO()
                            .setFieldName(hasLogicFieldArray ? logicFieldArray[fieldIndex++] : fieldName)
                            .setTableAlias(tableAlias)
                            .setCompare(ComparisonOperatorsEnum.EQ.getOperator())
                            .setFieldValue(IsDeletedEnum.NOT_DELETED.getValue());

                    list.add(dto);
                }
            } else {
                QueryConditionDTO dto = new QueryConditionDTO()
                        .setFieldName(fieldName)
                        .setCompare(ComparisonOperatorsEnum.EQ.getOperator())
                        .setFieldValue(IsDeletedEnum.NOT_DELETED.getValue());

                list.add(dto);
            }
        }
    }

    private static <T> void initUserConditionCondition(List<QueryConditionDTO> list, T t) {
        QueryUserCondition queryUserCondition = AnnotationUtils.getAnnotation(t.getClass(), QueryUserCondition.class);
        if (queryUserCondition != null) {
            String[] tableAliasArray = queryUserCondition.value();
            String userField = queryUserCondition.userField();

            if (ArrayUtil.isNotEmpty(tableAliasArray)) {
                for (String tableAlias : tableAliasArray) {
                    if (StrUtil.isNotBlank(tableAlias)) {
                        tableAlias += ".";
                    }
                    QueryConditionDTO dto = new QueryConditionDTO()
                            .setFieldName(userField)
                            .setTableAlias(tableAlias)
                            .setCompare(ComparisonOperatorsEnum.EQ.getOperator())
                            .setFieldValue(SecurityUtil.getUserId());

                    list.add(dto);
                }
            } else {
                QueryConditionDTO dto = new QueryConditionDTO()
                        .setFieldName(userField)
                        .setCompare(ComparisonOperatorsEnum.EQ.getOperator())
                        .setFieldValue(SecurityUtil.getUserId());

                list.add(dto);
            }
        }
    }

    private static boolean isIgnore(Object val) {
        if (val != null && !(val instanceof Map)) {
            return val instanceof String && StrUtil.isBlank((String) val);
        }
        return true;
    }

}
